package coupon;

import static org.mockito.Mockito.mock;

import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.builder.xml.XMLStatementBuilder;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.SimpleExecutor;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.parsing.PropertyParser;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.TextSqlNode;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLScriptBuilder;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.context.junit4.SpringRunner;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.alibaba.fastjson.JSONObject;

public static void main(String[] args) throws Exception {

        String script = "<script>select * from table where 1 = 1<if test='id != null'>and id = ${id} </if></script>";

        System.out.println(buildSql(script));

        }

private static String buildSql(String script) {

        LanguageDriver languageDriver = new XMLLanguageDriver();

        Configuration configuration = new Configuration();

        SqlSource sqlSource = languageDriver.createSqlSource(configuration, script, Object.class);

        Map<String, String> parameters = new HashMap<>();
        parameters.put("id", "1");

        BoundSql boundSql = sqlSource.getBoundSql(parameters);

        return boundSql.getSql();

        }

/**
 * 动态注册 Mapper
 * // 获取SqlSessionFactory实例
 * SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 *
 * // 获取Configuration实例
 * Configuration configuration = sqlSessionFactory.getConfiguration();
 *
 * // 获取MapperRegistry实例
 * MapperRegistry mapperRegistry = configuration.getMapperRegistry();
 *
 * // 注册Mapper接口
 * mapperRegistry.addMapper(UserMapper.class);
 * // 获取SqlSessionFactory实例
 * SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 *
 * // 获取Configuration实例
 * Configuration configuration = sqlSessionFactory.getConfiguration();
 *
 * // 判断MappedStatement id是否存在
 * if (configuration.getMappedStatement("com.example.mapper.UserMapper.selectUserById") != null) {
 *     // MappedStatement id存在
 * } else {
 *     // MappedStatement id不存在
 * }
 *
 * // 创建一个MappedStatement实例
 * MappedStatement ms = new MappedStatement.Builder(configuration, "com.example.mapper.UserMapper.selectUserById",
 *         // ...其他参数...
 * ).build();
 *
 * // 注册MappedStatement
 * configuration.addMappedStatement(ms);
 *
 *批量执行器
 * SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
 * try {
 *     UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
 *
 *     // 批量插入用户数据
 *     for (User user : userList) {
 *         userMapper.insertUser(user);
 *     }
 *
 *     // 提交事务
 *     sqlSession.commit();
 * } catch (Exception e) {
 *     // 回滚事务
 *     sqlSession.rollback();
 * } finally {
 *     // 关闭SqlSession
 *     sqlSession.close();
 * }
 *
 * Configuration configuration = sqlSessionFactory.getConfiguration();
 * MappedStatement mappedStatement = configuration.getMappedStatement("getUserById");
 * BoundSql boundSql = mappedStatement.getBoundSql(1L);
 * List<User> userList = sqlSessionFactory.openSession().selectList(mappedStatement.getId(), boundSql.getParameterObject());
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = com.huoli.app.MemberAccountAppMain.class)
public class TestDynamicMapper {

  @Autowired
  @Qualifier("skypaySqlSessionFactory")
  private SqlSessionFactory sessionFactory;

  static String script = "SELECT * FROM PAYINFO_DICT WHERE  1 = 1 "
      + " <if test=\"codeType != null and codeType != ''\"> " + "    and CODETYPE = #{codeType} "
      + " </if> " + " <if test=\"value != null and value != ''\"> " + " and VALUE = #{value} "
      + " </if>  and VALUE = #{value} ";

  public static String wrapScript(String script) {
    return "<script>" + script + "</script>";
  }

  @Test
  public void testMapper()
      throws SQLException, SAXException, IOException, ParserConfigurationException {
    Configuration configuration = sessionFactory.getConfiguration();
    Map<String, Object> params = new HashMap<>();
    params.put("codeType", "ip_white_list");
    params.put("value", "10.0.1.1");
    // 默认languageDriver is XMLLanguageDriver
    // org.apache.ibatis.builder.xml.XMLConfigBuilder.settingsElement(Properties) 设置语言驱动
    LanguageDriver languageDriver = configuration.getLanguageRegistry().getDefaultDriver();

    // 事务工厂 见 org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory()
    // this.transactionFactory = new SpringManagedTransactionFactory();
    // TransactionFactory transactionFactory =
    // configuration.getEnvironment().getTransactionFactory();
    // XMLLanguageDriver languageDriver = new XMLLanguageDriver();

    // issue #127
    /*
     * script = PropertyParser.parse(script, configuration.getVariables()); TextSqlNode textSqlNode
     * = new TextSqlNode(script);
     */
    // SqlSource sqlSource = new DynamicSqlSource(configuration, textSqlNode);
    SqlSource sqlSource =
        languageDriver.createSqlSource(configuration, wrapScript(script), params.getClass());
    /*
     * Document document = newDocumentBuilder().parse(new InputSource(new StringReader(script)));
     * XNode xNode = new XNode(new XPathParser(script), document.getFirstChild(),
     * configuration.getVariables());
     * 
     * XMLScriptBuilder builder = new XMLScriptBuilder(configuration, xNode, Map.class);
     */
    // SqlSource sqlSource = builder.parseScriptNode();

    /*
     * SqlSourceBuilder sourceBuilder = new SqlSourceBuilder(configuration); SqlSource sqlSource =
     * sourceBuilder.parse(script,Map.class,null) ;
     */
    String statementId = "demo_111";
    ResultMap inlineResultMap = new ResultMap.Builder(configuration, statementId, Map.class,
        new ArrayList<ResultMapping>(), true).build();


    // KeyGenerator keyGenerator = entityMapper.autoId() ? new Jdbc3KeyGenerator() : new
    // NoKeyGenerator();

    // 构建语句
    MappedStatement.Builder statementBuilder =
        new MappedStatement.Builder(configuration, statementId, sqlSource, SqlCommandType.SELECT)
            // .resource(resource)
            // .fetchSize(fetchSize)
            // .timeout(timeout)
            .statementType(StatementType.PREPARED).keyGenerator(NoKeyGenerator.INSTANCE)
            // .keyProperty(keyProperty)
            // .keyColumn(keyColumn)
            // .databaseId(databaseId)
            .lang(languageDriver).resultOrdered(false).resultMaps(Arrays.asList(inlineResultMap))
    // .resultSetType(ResultSetType.SCROLL_INSENSITIVE)

    // .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
    // .useCache(valueOrDefault(useCache, isSelect))
    // .cache(currentCache);
    ;

    // 设置参数映射
    List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
    ParameterMap parameterMap =
        new ParameterMap.Builder(configuration, statementId, params.getClass(), parameterMappings)
            .build();
    // ParameterMap statementParameterMap = parameterMap;
    statementBuilder.parameterMap(parameterMap);
    MappedStatement statement = statementBuilder.build();

    // configuration.addMappedStatement(statement);
    Environment environment = configuration.getEnvironment();
    TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    // environment.getDataSource().getConnection().getTransactionIsolation();
    Transaction tx = transactionFactory.newTransaction(environment.getDataSource(),
        TransactionIsolationLevel.REPEATABLE_READ, false);
    SimpleExecutor executor = new SimpleExecutor(configuration, tx);
    // List<Object> list = executor.query(statement, 16, RowBounds.DEFAULT, null);

    String sqlString = sqlSource.getBoundSql(params).getSql();
    System.out.println("sql=" + sqlString);
    List<Object> list =
        executor.doQuery(statement, params, RowBounds.DEFAULT, null, sqlSource.getBoundSql(params));
    System.out.println(JSONObject.toJSONString(list));
  }

  /**
   * org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromConnection(
   * ExecutorType, Connection)
   * 
   * @param environment
   * @return
   */
  private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }


  private MappedStatement getQueryIdsMappedStatementForUpdateCache(MappedStatement mt ) {
     String msId = mt.getId() + "_aaa";

    MappedStatement statement = null;
    Configuration configuration = mt.getConfiguration();
    try {
      statement = configuration.getMappedStatement(msId);
      if (statement != null) {
        return statement;
      }
    } catch (Exception e) {
    }

    synchronized (configuration) {
      /*if (configuration.hasStatement(msId)) {
        return configuration.getMappedStatement(msId);
      }

      String sql = entityInfo.getMapperSqls().get(mt.getId());*/

    /*  if (StringUtils.isNotBlank(sql)) {
        if (!sql.toLowerCase().contains(entityInfo.getTableName().toLowerCase())) {
          return null;
        }
        sql = "select " + entityInfo.getIdColumn() + " from " + entityInfo.getTableName()
            + " WHERE " + sql.split(WHERE_REGEX)[1];
        sql = String.format(SqlTemplate.SCRIPT_TEMAPLATE, sql);
      } else {
        sql = PARSE_SQL_ERROR_DEFAULT;
      }*/
      String sql = "";
      SqlSource sqlSource = configuration.getDefaultScriptingLanguageInstance()
          .createSqlSource(configuration, sql, Object.class);

      MappedStatement.Builder statementBuilder =
          new MappedStatement.Builder(configuration, msId, sqlSource, SqlCommandType.SELECT);

      statementBuilder.resource(mt.getResource());
      statementBuilder.fetchSize(mt.getFetchSize());
      statementBuilder.statementType(mt.getStatementType());
      statementBuilder.parameterMap(mt.getParameterMap());
      statement = statementBuilder.build();

      List<ResultMap> resultMaps = new ArrayList<ResultMap>();

      String id = msId + "-Inline";
     /* ResultMap.Builder builder = new ResultMap.Builder(configuration, id, entityInfo.getIdType(),
          new ArrayList<ResultMapping>(), true);
      resultMaps.add(builder.build());
      MetaObject metaObject = SystemMetaObject.forObject(statement);
      metaObject.setValue("resultMaps", Collections.unmodifiableList(resultMaps));*/

      configuration.addMappedStatement(statement);

      return statement;
    }
  }

}
